﻿/**
* Класс реализует слайдшоу
* @author Firsoff Maxim, ICQ:235859730, email: firsoff_maxim@mail.ru
* @version 0.1/08.02.2008 11:04
* @since Action Script 3.0
*/

package ru.vega_int.firsoff.as3.slideshow
{
	import flash.display.Sprite;
	import flash.errors.IllegalOperationError;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import ru.vega_int.firsoff.as3.events.*;
	import ru.vega_int.firsoff.as3.slideshow.*;
	
	
	public class SlideShow extends Sprite
	{
		
		//	PROPERTIES
		//	private
		private var content:Array;					//	контент слайдшоу
		private var _height:Number;					//	высота содежимого контейнера
		private var _width:Number;					//	ширина содежимого контейнера
		private var time_switch:Number;				//	время переключения
		private var time_show:Number;				//	время показа
		private var time_destroy:Number;			//	время уничтожения
		private var active:SlideShowContainer;		//	изображение которое показывается сейчас
		private var passive:SlideShowContainer;		//	изображение которое скрывается
		private var randomPlay:Boolean;				//	случайное проигрывание
		private var timer:Timer;					//	таймер
		private var pointer:uint;					//	указатель
		private var totalPreloadFiles:int;			//	колво файлов для предазгрузки
		private var isPreloadComplete:Boolean;		//	презагрузка окончена?
		private var counterPreloadFiles:uint;		//	колво загруженных в кеш
		private var _containers:Array;				//	массив контейнеров
		private var _target:Sprite;					//	контейнер для слайдшоу
		private var autoStart:Boolean;				//	авто запуск после предзагрузки
		private var direction:String;				//	направление просмотра BACK/FORVARD
		
		private const DEFAULT_WIDTH:Number = 200;			//	ширина по умолчанию
		private const DEFAULT_HEIGHT:Number = 100;			//	высота по умолчанию
		private const DEFAULT_RANDOM_PLAY:Boolean = false;	//	случайный порядок
		private const DEFAULT_TIME_SHOW:Number = 4;			//	время показа
		private const DEFAULT_TIME_SWITCH:Number = 0.5;		//	время переключения
		private const DEFAULT_PRELOAD_FILES:Number = 2;		//	колво файлов для предзагрузки
		private const TOTAL_CONTAINERS:uint = 6;			//	колво контейнеров
		private const DEFAULT_TIME_DESTROY:Number = 0.3;	//	время уничтожения
		
		//	public
		public var errorUrl:String;
		/**
		 * Конструктор
		 * @param	content	Контент
		 * @param	initObj	Инициализация
		 * @param 	initObj._width	Ширина
		 * @param 	initObj._height	Высота
		 * @param 	initObj.randomPlay	Случайный список
		 * @param 	initObj.time_switch	Время переключения
		 * @param 	initObj.time_show	Время переключения
		 * @param 	initObj.totalPreloadFiles	Колво файлов для предзагрузки
		 * @param 	initObj.autoStart	автостарт
		 * @param 	initObj.time_destroy	Время фейдинга при уничтожении
		 */
		public function SlideShow(content:Array, initObj:Object, target:Sprite=null) 
		{
			this.content = content;
			if(initObj._width){this._width = Math.abs(Number(initObj._width));}
			else
			{
				throw new IllegalOperationError('Width of slideshow is not correct');
			};
			
			if(initObj._height){this._height = Math.abs(Number(initObj._height));}
			else
			{
				throw new IllegalOperationError('Height of slideshow is not correct');
			};
			
			this.randomPlay = Boolean(initObj.randomPlay);
			
			
			if(initObj.time_switch)
			{
				this.time_switch = Math.abs(Number(initObj.time_switch));	
			}
			else
			{
				this.time_switch = DEFAULT_TIME_SWITCH;
			};
			
			if(initObj.time_show)
			{
				this.time_show = Math.abs(Number(initObj.time_show));	
			}
			else
			{
				this.time_show = DEFAULT_TIME_SHOW;
			};
			this.time_show = Math.abs(time_show + time_switch * 2) * 1000;
			
			if(initObj.totalPreloadFiles is Number)
			{
				this.totalPreloadFiles = initObj.totalPreloadFiles;
			}
			else
			{
				this.totalPreloadFiles = DEFAULT_PRELOAD_FILES;
			};
			
			this.totalPreloadFiles = initObj.totalPreloadFiles;
			//	защита от дурака
			if(totalPreloadFiles > content.length){this.totalPreloadFiles = content.length;};
			
			if(initObj.autoStart is Boolean){this.autoStart = initObj.autoStart;}
			else{this.autoStart = false;};
			
			if(initObj.time_destroy) 
			{
				this.time_destroy = initObj.time_destroy;
			}
			else 
			{
				this.time_destroy = DEFAULT_TIME_DESTROY;
			};
			this._target = target;
			init();
		};
		
		//	MEHODS
		
		//	private
		private function init():void
		{
			pointer = 0;
			counterPreloadFiles = 0;
			isPreloadComplete = false;
			direction = 'FORVARD';
			_target.addChild(this);
			_containers = new Array();
			//	если случайный список, то произведем случайную сортировку
			if(randomPlay)
			{
				content = randomArray(content);
			};
			createContainers();	//	создаем контейнеры
			load();
			timer = new Timer(time_show, 999);
			timer.addEventListener(TimerEvent.TIMER, timerHandler);
		};
		
		/**
		 * Генерация массива со случайным положением членов
		 * @param	a	Входной массив
		 * @return	Новый массив
		 */
		private function randomArray(a:Array):Array
		{
			var output:Array = new Array();		//	выходной массив
			var input:Array = a;				//	входной
			var indexArray:Array = new Array();	//	массив индексов
			var total:uint = input.length;		//	общее колво элементов входного массива
			if(total > 1)
			{
				//	создаем массив индексов
				for(var i:uint=0; i < input.length; i++){indexArray.push(i);};
				//	сортируем случайным образом индексы и заносим в выходной массив данные
				while (total) 
				{
					var r:uint = Math.floor(Math.random() * (total));
					output.push(input[indexArray[r]]);
					indexArray.splice(r, 1);
					total--;
				};
				/*for(var j:Number = 0; j< output.length; j++)	//	проверка
				{
					trace(output[j].url)
				};*/
			}
			else{output = input;};
			return output;
		};
		
		/**
		 * Начинает процесс загрузки
		 */
		private function load():void
		{
			// определяем контент как правило это диапазон ссылок - [-2, 2] в массиве
			var loading_content:Array = new Array();
			var length:uint = content.length;
			if(content.length < TOTAL_CONTAINERS)
			{
				loading_content = content;
			}
			else
			{
				loading_content.push(content[0]);
				loading_content.push(content[1]);
				loading_content.push(content[2]);
				loading_content.push(content[length - 1]);
				loading_content.push(content[length - 2]);
				//trace(loading_content[0].url)
			};
			//trace('_containers.length = '+_containers.length);
			for (var i:int = 0; i < _containers.length; i++) 
			{
				var c:SlideShowContainer = SlideShowContainer(_containers[i]);
				//	подписываемся на загрузку
				c.addEventListener(SlideShowContainerEvents.LOAD, loadHandler);
				//trace('loading_content[i] - '+loading_content[i])
				if(loading_content[i])
				{
					c.load(loading_content[i].url);
				};
			};
		};
		
		private function removeContainerHandlers(c:SlideShowContainer=null):void
		{
			if(c)
			{
				c.removeEventListener(SlideShowContainerEvents.ERROR, errorHandler);
				c.removeEventListener(SlideShowContainerEvents.LOAD, loadHandler);
				c.removeEventListener(ProgressEvent.PROGRESS, waitProgressHandler);
				c.removeEventListener(SlideShowContainerEvents.LOAD, waitLoadHandler);
				c.removeEventListener(SlideShowContainerEvents.HIDE, waitLoadHandler);
				c.removeEventListener(SlideShowContainerEvents.SHOW, handleShow);
				
			};
		};
		
		private function createContainers():void
		{
			for(var i:uint=0; i < TOTAL_CONTAINERS; i++)
			{
				var cont:SlideShowContainer = new SlideShowContainer(this._width, this._height);
				addChild(cont);
				cont.setFadeTime(this.time_switch);			//	устанавливаем время переключения
				_containers.push(cont);
			};
		};
		
		//	<---------------- HANDLERS
		/**
		 * Обработчик события загрузки в кеш 1 файла
		 * @param	e
		 */
		private function cacheLoadingHandler(e:Event):void
		{
			
		};
		
		private function timerHandler(e:TimerEvent):void
		{
			next();
		};
		
		private function getContainer(id:String):SlideShowContainer
		{
			var ret:SlideShowContainer;
			for each(var c:SlideShowContainer in _containers) 
			{
				//trace(c.url +' / '+ id + 'rezult: ' + )
				if(c.url == id)
				{
					ret =  c;
					break;
				};
			};
			return ret;
		};
		
		/**
		 * Скрытие всех неиспользованных контейнеров
		 */
		private function hideUnusedContainer():void 
		{
			for each(var c:SlideShowContainer in _containers) 
			{
				//trace(c != active && c != passive)
				if(c != active && c != passive)
				{
					c.alpha = 0;
				};
			};
		};
		
		/**
		 * Функция ищет свободный контейнер и в соответствии с id
		 * подгружает заранее файл
		 * @param	direction Текущее направление просмотра - BACK/FORVARD
		 */
		private function preloadFile():void
		{
			var url:String;							//	урл для загрузки
			var container:SlideShowContainer;		//	контейнер для загрузки
			var index:int;	
			if(direction == 'FORVARD'){index = pointer + 2;}
			else {index = pointer - 2;};
			if (index < 0) 
			{
				index = index + content.length;
			};
			if (index > (content.length - 1)) 
			{
				index = index - (content.length);
			};
			url = content[index].url;
			
			try 
			{
				container = getEmptyContainer();
				container.load(url);
			}
			catch (e:Error)
			{
				
			};
			
		};
		
		private function getEmptyContainer():SlideShowContainer
		{
			var ret:SlideShowContainer;
			for (var i:int = 0; i < _containers.length; i++) 
			{
				if (SlideShowContainer(_containers[i]).state == SlideShowContainerStates.EMPTY) 
				{
					ret = _containers[i];
					break;
				};
			};
			return ret;
		};
		// HANDLERS ------------------------------>
		/**
		 * Обработчик загрузки контейнера
		 * @param	e
		 */
		private function loadHandler(e:SlideShowContainerEvents):void
		{
			//trace('container LOAD');
			var c:SlideShowContainer = SlideShowContainer(e.target);
			c.removeEventListener(SlideShowContainerEvents.LOAD, loadHandler);
			counterPreloadFiles++;
			if(counterPreloadFiles == totalPreloadFiles)
			{
				isPreloadComplete = true;
				if(autoStart)				//	если выставлен автостарт - запуск слайдшоу
				{
					this.start();
				};
				this.dispatchEvent(new SlideShowEvents(SlideShowEvents.PRELOAD_COMPLETE));
			};
		};
		
		/**
		 * Обработчик вызывается когда загрузился ОЖИДАЕМЫЙ контейнер
		 * @param	e
		 */
		private function waitLoadHandler(e:SlideShowContainerEvents):void
		{
			//trace('wait image loaded');
			var c:SlideShowContainer = SlideShowContainer(e.target);
			//	удаляем все не нужные слушатели
			c.removeEventListener(SlideShowContainerEvents.LOAD, waitLoadHandler);
			c.removeEventListener(ProgressEvent.PROGRESS, waitProgressHandler);
			// запускаем таймер
			timer.start();
			show(pointer);
		};
		
		/**
		 * Обработчик прогресса загрузки
		 * @param	e
		 */
		private function waitProgressHandler(e:ProgressEvent):void
		{
			//trace('container PROGRESS');
			this.dispatchEvent(e);		//	генерируем событие загрузки ожидаемой фотки
		};
		
		/**
		 * Обработчик ошибки
		 * @param	e
		 */
		private function errorHandler(e:SlideShowContainerEvents):void
		{
			trace('container ERROR');
			errorUrl = SlideShowContainer(e.target).url;
			this.dispatchEvent(new SlideShowContainerEvents(SlideShowContainerEvents.ERROR));
		};
		
		/**
		 * Обработчик показа контейнера
		 * @param	e
		 */
		private function handleShow(e:SlideShowContainerEvents):void
		{
			//passive.state = SlideShowContainerStates.EMPTY;
			var c:SlideShowContainer = SlideShowContainer(e.target);
			c.removeEventListener(SlideShowContainerEvents.SHOW, handleShow);
			
			
			//hideUnusedContainer();			//	на всякий случай скрываем неиспользуемые
			
		};
		
		/**
		 * Обработчик скрытия контейнера
		 * @param	e
		 */
		private function handleHide(e:SlideShowContainerEvents):void
		{
			var c:SlideShowContainer = SlideShowContainer(e.target);
			c.removeEventListener(SlideShowContainerEvents.HIDE, handleHide);
			c.state = SlideShowContainerStates.EMPTY;
			preloadFile();
		};
		
		private function handleDestroy(e:Event):void
		{
			if(this.alpha > 0)
			{
				this.alpha -= 0.2;
			}
			else 
			{
				this.alpha = 0;
				this.removeEventListener(Event.ENTER_FRAME, handleDestroy);
				for(var i in _containers)
				{
					
					_containers[i].destroy();
					this.removeChild(_containers[i]);
					
				};
				_containers = null;
				_target.removeChild(this);
			};
		};
		//	<------------------------
		
		//	public
		
		/**
		 * Старт слайдшоу, всегда запускает с 0 изображения
		 */
		public function start():void
		{
			//trace('SLIDE SHOW START')
			pointer = 0;
			show(pointer);				//	запуск с нулевого элемента
			timer.reset();				//	сброс таймера 
			timer.start();				//	запуск таймера
		};
		
		
		/**
		 * ПОказ изображения
		 * @param	index	Индекс изображения (порядковый номер)
		 */
		public function show(index:uint):void
		{
			// получаем идентификатор по входному индексу
			var id:String = content[index].url;
			if(active)
			{
				active.hide();
			};
			var currCont:SlideShowContainer = getContainer(id);
			this.dispatchEvent(new SlideShowEvents(SlideShowEvents.CHANGE));
			removeContainerHandlers(active);
			//	в зависимости от текущего состояние контейнера
			//trace('state : '+currCont.state + ' URL: ' +currCont.url);
			switch (currCont.state) 
			{
				case SlideShowContainerStates.LOADING:
					//	генерируем событие об ожидании загрузки
					timer.stop();
					this.dispatchEvent(new SlideShowEvents(SlideShowEvents.WAIT_LOADING));
					currCont.addEventListener(SlideShowContainerEvents.LOAD, waitLoadHandler);
					currCont.addEventListener(ProgressEvent.PROGRESS, waitProgressHandler);
					
				break;
				
				case SlideShowContainerStates.COMPLETE:
				//trace('state : '+currCont.state + ' URL: ' +currCont.url);
				
					if (active) 
					{
						passive = active;
						passive.addEventListener(SlideShowContainerEvents.HIDE, handleHide);
						passive.hide();
					};
					active = currCont;
					active.addEventListener(SlideShowContainerEvents.SHOW, handleShow);
					active.show();
					addChild(active);
					
				break;
				
				case SlideShowContainerStates.ERROR:
					
				break;
			};
			//trace('currCont : ' + currCont);
		};
		
		/**
		 * Останов слайдшоу
		 */
		public function stop():void
		{
			
		};
		
		/**
		 * Переход к следующей фотке
		 */
		public function next():void
		{
			direction = 'FORVARD';
			pointer++;
			if(pointer > content.length - 1){pointer = 0;};
			show(pointer);
		};
		
		/**
		 * Переход к предыдущей фотке
		 */
		public function prev():void
		{
			direction = 'BACK';
			pointer--;
			if(pointer < 0){pointer = content.length - 1;};
			show(pointer);
		};
		
		/**
		 * Уничтожает все текущие загрузки, очищает все объекты созданные при работе экземпляра
		 */
		public function destroy():void
		{
			//trace('DESTROY');
			//trace(this.parent.name)
			var _fadeStep:Number = 1/(time_destroy * this.stage.frameRate);
			this.addEventListener(Event.ENTER_FRAME, handleDestroy);
			timer.stop();
			timer.removeEventListener(TimerEvent.TIMER, timerHandler);
		};
	};
};
